package study.ttl.service.impl;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.revinate.guava.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import study.ttl.common.Constants;
import study.ttl.common.config.AliPayConfig;
import study.ttl.common.enums.LogModuleEnum;
import study.ttl.common.enums.OrderStatusEnum;
import study.ttl.common.enums.OrderTypeEnum;
import study.ttl.common.system.AsyncTaskFactory;
import study.ttl.entity.Goods;
import study.ttl.entity.Orders;
import study.ttl.entity.User;
import study.ttl.exception.CustomException;
import study.ttl.mapper.GoodsMapper;
import study.ttl.mapper.OrdersMapper;
import study.ttl.service.OrdersService;
import study.ttl.utils.SaUtils;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class OrdersServiceImpl implements OrdersService , InitializingBean {

    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private GoodsMapper goodsMapper;

    private static final ConcurrentHashMap<Integer, Object> orderMap = new ConcurrentHashMap<>();

    private RateLimiter limiter;

    @Autowired
    private AliPayConfig aliPayConfig;



    /**
     * 添加订单 针对普通订单和团购订单
     * 秒杀比较特殊，秒杀有单独的数量，需要单独校验，秒杀需要做单独的高并发处理
     * @param orders
     */
    @Override
    @Transactional
    public Orders add(Orders orders) {
        // 获取当前登录用户
        User loginUSer = SaUtils.getLoginUSer();
        // 订单锁怎么做
        // 类锁 效率很低 只能是一个一个进行 不能保证在同时处理多个订单
        // 采取分段锁 什么是分段锁 锁是业务编号   比如用户id  订单编号
        // 当orderMap里面有值，则获取锁，如果没有值，则创建锁并获得锁
        Object lock = orderMap.computeIfAbsent(loginUSer.getId(), k -> new Object());
        // 加锁  保证同一人不能并发下单，同一时间只能下单一次
        synchronized (lock) {
            // 判断商品是否存在
            Goods goods = goodsMapper.selectById(orders.getGoodsId());
            if (goods == null) {
                throw new CustomException("商品不存在");
            }

            // 判断库存是否足够
            int store = goods.getStore() - orders.getNum();
            if (store < 0) {
                throw new CustomException("库存不足");
            }

            // 团购订单，要查询团购信息
            // 团购订单id
            Integer groupOrderId = orders.getGroupOrderId();
            Orders groupOrder = null;
            if (ObjectUtil.isNotNull(groupOrderId)) {
                groupOrder = this.selectById(groupOrderId);
                if (ObjectUtil.isNull(groupOrder) || !OrderStatusEnum.IN_GROUP.name().equals(groupOrder.getStatus())) {
                    throw new CustomException("团购订单不存在或已结束");
                }
                if (!groupOrder.getGoodsId().equals(orders.getGoodsId())) {
                    throw new CustomException("拼团信息错误");
                }
            }
            goods.setStore(store);
            // 更新商品库存
            goodsMapper.updateById(goods);

            orders.setGoodsName(goods.getName());
            orders.setGoodsImg(goods.getImg());
            if (OrderTypeEnum.COMMON.name().equals(orders.getType())) {
                orders.setGoodsPrice(goods.getOriginPrice());
                orders.setTotal(goods.getOriginPrice().multiply(BigDecimal.valueOf(orders.getNum())));
            } else if (OrderTypeEnum.GROUP.name().equals(orders.getType())) {
                orders.setGoodsPrice(goods.getGroupPrice());
                orders.setTotal(goods.getGroupPrice().multiply(BigDecimal.valueOf(orders.getNum())));
            }
            // 生成订单号
            String orderNo = IdUtil.getSnowflakeNextIdStr();
            orders.setOrderNo(orderNo);
            orders.setUserId(loginUSer.getId());
            orders.setCreateTime(DateUtil.now());
            orders.setStatus(OrderStatusEnum.NOT_PAY.name());
            // 插入订单
            ordersMapper.insert(orders);

            // 更新团购订单
            if (ObjectUtil.isNotNull(groupOrder)) {
                groupOrder.setGroupOrderId(orders.getId());
                this.update(groupOrder);
            }
        }

        // 记录日志
        AsyncTaskFactory.recordLog(LogModuleEnum.ORDER.value, "创建订单完成。【" + orders.getOrderNo() + "】", loginUSer.getId());
        // 释放锁
        orderMap.remove(loginUSer.getId());
        return orders;
    }


    /**
     * 添加秒杀订单
     * 处理高并发
     * @param orders
     * @return
     */
    @Transactional
    @Override
    public Orders addFlashOrder(Orders orders) {
        // 设置阻塞，限制秒杀请求
        limiter.acquire();
        // 获取当前登录用户
        User loginUSer = SaUtils.getLoginUSer();
        // 订单锁怎么做
        // 类锁 效率很低 只能是一个一个进行 不能保证在同时处理多个订单
        // 采取分段锁 什么是分段锁 锁是业务编号   比如用户id  订单编号
        // 当orderMap里面有值，则获取锁，如果没有值，则创建锁并获得锁
        Object lock = orderMap.computeIfAbsent(loginUSer.getId(), k -> new Object());
        // 加锁  保证同一人不能并发下单，同一时间只能下单一次
        synchronized (lock) {
            // 判断商品是否存在
            Goods goods = goodsMapper.selectById(orders.getGoodsId());
            if (goods == null) {
                throw new CustomException("商品不存在");
            }

            if (goods.getFlashNum() < orders.getNum()) {
                throw new CustomException("秒杀商品已抢完");
            }

            // 判断库存是否足够
            int store = goods.getStore() - orders.getNum();
            if (store < 0) {
                throw new CustomException("库存不足");
            }

            goods.setFlashNum(goods.getFlashNum() - orders.getNum());
            goods.setStore(store);
            // 更新商品库存
            goodsMapper.updateById(goods);

            // 生成订单号
            String orderNo = IdUtil.getSnowflakeNextIdStr();
            orders.setGoodsName(goods.getName());
            orders.setGoodsImg(goods.getImg());
            orders.setOrderNo(orderNo);
            orders.setUserId(loginUSer.getId());
            orders.setCreateTime(DateUtil.now());
            orders.setStatus(OrderStatusEnum.NOT_PAY.name());
            orders.setGoodsPrice(goods.getFlashPrice());
            orders.setTotal(goods.getFlashPrice().multiply(BigDecimal.valueOf(orders.getNum())));
            // 插入订单
            ordersMapper.insert(orders);

        }

        // 记录日志
        AsyncTaskFactory.recordLog(LogModuleEnum.ORDER.value, "创建订单完成。【" + orders.getOrderNo() + "】", loginUSer.getId());
        // 释放锁
        orderMap.remove(loginUSer.getId());
        return orders;
    }

    /**
     * 根据订单号查询订单
     * @param orderNo
     * @return
     */
    @Override
    public Orders selectByOrderNo(String orderNo) {
        Orders orders = ordersMapper.selectByOrderNo(orderNo);
        return orders;
    }

    /**
     * 根据状态查询订单
     * @param status
     * @return
     */
    @Override
    public List<Orders> selectByStatus(String status) {
        List<Orders> ordersList = ordersMapper.selectByStatus(status);
        return ordersList;
    }

    /**
     * 取消订单
     * @param orders
     */
    @Transactional
    @Override
    public void cancelOrder(Orders orders) {
        // 将订单状态改为取消
        orders.setStatus(OrderStatusEnum.CANCEL.name());
        // 释放库存
        Goods goods = goodsMapper.selectById(orders.getGoodsId());
        if (ObjectUtil.isNotNull(goods)) {
            goods.setStore(goods.getStore() + orders.getNum());
            goodsMapper.updateById(goods);
        }
        ordersMapper.updateById(orders);
    }

    /**
     * 取消团购订单
     * @param orders
     */
    @Override
    @Transactional
    public void cancelGroupOrder(Orders orders) {
        // 退款
        // 1. 创建Client，通用SDK提供的Client，负责调用支付宝的API
        AlipayClient alipayClient = new DefaultAlipayClient(Constants.ALIPAY_GATEWAY_URL, aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(), Constants.ALIPAY_FORMAT, Constants.ALIPAY_CHARSET, aliPayConfig.getAlipayPublicKey(), Constants.ALIPAY_SIGN_TYPE);

        // 2. 创建 Request并设置Request参数
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setNotifyUrl(aliPayConfig.getNotifyUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.set("out_trade_no", orders.getOrderNo());  // 我们自己生成的订单编号 必须是不重复的订单
        bizContent.set("refund_amount", orders.getTotal()); // 订单的总金额
        bizContent.set("trade_no", orders.getPayNo()); // 支付宝支付订单号
        bizContent.set("out_request_no", IdUtil.fastSimpleUUID());   // 随机数
        request.setBizContent(bizContent.toString());
        try {
            // 退款调用接口
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("订单号【{}】退款成功", orders.getOrderNo());
                this.cancelOrder(orders);
            }
        } catch (AlipayApiException e) {
            log.error("退款失败", e);
        }
    }

    /**
     * 分页查询团购订单
     * @param pageNum
     * @param pageSize
     * @param orders
     * @return
     */
    @Override
    public PageInfo<Orders> selectGroupPage(Integer pageNum, Integer pageSize, Orders orders) throws ParseException {
        PageHelper.startPage(pageNum, pageSize);
        List<Orders> ordersList = ordersMapper.selectAllGroup(orders);
        for (Orders dbOrder : ordersList) {
            if (ObjectUtil.isNotEmpty(dbOrder.getPayTime())) {
                long now = System.currentTimeMillis();
                Date pay = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dbOrder.getPayTime());
                long gap = (pay.getTime() + 24 * 60 * 60 * 1000 - now ) / 1000;
                dbOrder.setMaxTime(gap);
            }
        }
        return PageInfo.of(ordersList);
    }

    /**
     * 根据id删除订单
     * @param id
     */
    @Override
    public void deleteById(Integer id) {
        ordersMapper.deleteById(id);
    }


    /**
     * 修改订单信息
     * @param orders
     */
    @Override
    public void update(Orders orders) {
        ordersMapper.updateById(orders);
    }

    /**
     * 根据id查询订单
     * @param id
     * @return
     */
    @Override
    public Orders selectById(Integer id) {
        Orders orders = ordersMapper.selectById(id);
        return orders;
    }

    /**
     * 查询所有订单
     * @return
     */
    @Override
    public List<Orders> selectAll(Orders orders) {
        List<Orders> userList = ordersMapper.selectAll(orders);
        return userList;
    }

    /**
     * 分页查询订单
     * @param pageNum
     * @param pageSize
     * @param orders
     * @return
     */
    @Override
    public PageInfo<Orders> selectPage(Integer pageNum, Integer pageSize, Orders orders) {
        PageHelper.startPage(pageNum, pageSize);
        List<Orders> ordersList = ordersMapper.selectAll(orders);
        return PageInfo.of(ordersList);
    }

    /**
     * 批量删除订单
     * @param ids
     */
    @Override
    public void deleteBatch(List<Integer> ids) {
        ordersMapper.deleteBatch(ids);
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        limiter = RateLimiter.create(Constants.FLASH_LiMIT_NUM);
    }
}
